package lambda; /**
 * @author： zhangnan
 * @date： 2025/5/20 15:42
 * @description： 函数式调用测试类
 * @modifiedBy：
 * @version: 1.0
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 函数式接口：接口中只有一个未实现的方法，这个接口就叫做函数式接口
 * 只要是 函数式接口 就可以用lambda表达式简化
 *
 * @FunctionalInterface 检测注解，可以快速检测接口是否是函数式接口
 */
@FunctionalInterface
interface MyInterface {
    String hello();
}

@FunctionalInterface
interface MyInterface1 {
    int sum(int i);
}

@FunctionalInterface
interface MyInterface2 {
    int sum(int i, int j);
}

// 实现接口的方式
// 方式 1. 自己实现
//class myInterfaceImpl implements lambda.MyInterface1 {
//
//    @Override
//    public int sum(int i, int j) {
//        return i + j;
//    }
//}
// 方式 2：使用匿名实现类


public class TestFunction {


    /**
     * java.util.function 下已经准备一些现成函数式接口
     * 只要是 函数式接口 就可以用lambda表达式简化
     */

    public static void main(String[] args) {
        // lambda表达式简化实例的创建
        // 方式 2：使用匿名实现类 完整写法：参数列表 + 箭头 + 方法体
        // 简化写法：
        //      1. 参数类型可以不写,只写(参数名)，如 (int i,int j)可以简写成(i,j)，
        //      2. 方法体如果只有一行代码， 大括号{} 可以省略不写
        // 写法1：当没有入参时直接写()
        MyInterface m1 = () -> "hello,world!";
        System.out.println(m1.hello());
        MyInterface m2 = () -> {
            return "hello,hehe!";
        }; // {}也可不省
        System.out.println(m2.hello()); // 函数式调用实际调用的是方法体
        // 写法3：当只有一个入参时，() 和 参数类型 都可省略
        MyInterface1 m5 = i -> i << 1;
        System.out.println(m5.sum(2));// 调用上面那行的实现；
        // 写法2：当入参大于1个的时候，参数需要写；当实现的方法体只有一行代码是花括号{}可以省略不写
        MyInterface2 m6 = (i, j) -> i + j;
        System.out.println(m6.sum(1, 2));

        // :: 调用
        // 右边是固定的，左边就可以用var
        List<String> names = new ArrayList<String>();
        names.add("A");
        names.add("B");
        names.add("C");
        names.add("D");
        Collections.sort(names, (o1, o2) -> o2.compareTo(o1)); // 如果是灰色，说明可以直接使用方法引用的方式
        // 类::方法;  引用类中的实例方法
        Collections.sort(names, String::compareTo); // 如果是灰色，说明可以直接使用方法引用的方式
        System.out.println(names);

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("哈哈");
            }
        }).start();

        new Thread(() -> System.out.println("嘿嘿")).start();

        // 有入参,无出参（消费者）：function.accept
        Consumer<Integer> c = i -> System.out.println("参数：" + i);
        c.accept(1); // 调用的是函数的方法体：System.out.println("参数：" + i)
        // 有入参，有出参（多功能函数）
        Function<String, String> f = i -> "say: " + i;
        System.out.println(f.apply("hello"));
        // 无入参，无出参（普通函数）
        Runnable r = () -> System.out.println("let's run!");
        new Thread(r).start();
        // 无入参，有出参（提供者）
        Supplier<String> s = () -> "haha";
        System.out.println(s.get());
        // 断言：返回true或false
        Predicate<Integer> p = in -> in % 2 == 0;
        System.out.println(p.test(6)); // 判断6是否是偶数
        System.out.println(p.negate().test(6)); // 方向判断，即 判断6是否 不是偶数


        /**
         * 函数式调用 实战
         */
        // 1. 定义数据提供者函数
        Supplier<String> ss = () -> "42";
        // 2. 断言：验证是否一个数字
        Predicate<String> ps = str -> str.matches("-?\\d+(\\.\\d+)?");
        // 3. 转换器：把字符串变成数字， 类::实例方法 （静态方法）
//        Function<String, Integer> fs = str -> Integer.parseInt(str); 或 下面的方式都可以
        // 3.1 类::实例方法 （静态方法）
        Function<String, Integer> fs = Integer::parseInt;
        // 4. 消费者： 打印数字
        Consumer<Integer> cs = in -> {
            if (in % 2 == 0) {
                System.out.println("偶数： " + in);
            } else {
                System.out.println("奇数： " + in);
            }
        };
        System.out.println("====================");
        // 5.  串在一起，实现判断42 这个字符串是奇数还是偶数
//        if (ps.test(ss.get())) {
//            cs.accept(fs.apply(ss.get()));
//        } else {
//            System.out.println("非法数字");
//        }
        // 5.1 将5 中的逻辑抽象成一个方法，然后直接使用lambda表达式的方式调用
        extracted(() -> "7a7",
                str -> str.matches("-?\\d+(\\.\\d+)?"),
                Integer::parseInt,
                System.out::println);

    }

    private static void extracted(Supplier<String> ss, Predicate<String> ps, Function<String, Integer> fs, Consumer<Integer> cs) {
        if (ps.test(ss.get())) {
            cs.accept(fs.apply(ss.get()));
        } else {
            System.out.println("非法数字");
        }
    }


}



